//------------------------------------------------
// hazard.v
//
// James Forrest, 2013
// Based on code by:
// David_Harris@hmc.edu 3 November 2005
//
// Pipelined MIPS processor
//------------------------------------------------

module hazard(input  [4:0] rsD, rtD, rsE, rtE, 
              input  [4:0] writeregE, writeregM, writeregW,
              input        regwriteE, regwriteM, regwriteW,
              input        cp0ToRegE, memtoregE, memtoregM,
			  input        memwriteD, memwriteM, memwriteW,
              input        pcsrcD, branchD, cp0JumpM,
			  input  [1:0] jumpD,
              output           forwardaD, forwardbD,
              output reg [1:0] forwardaE, forwardbE,
			  output           forwardM,
              output       stallF, stallD, stallE, stallM, flushD, flushE, flushM, flushW,
              input        instrAckF, dataAckM
              );

  wire lwstallD, branchstallD, jumpstallD;

  // forwarding sources to D stage (branch equality)
  assign forwardaD = (rsD !=0 & rsD == writeregM & regwriteM);
  assign forwardbD = (rtD !=0 & rtD == writeregM & regwriteM);

  // forwarding sources to E stage (ALU)
  always @(*)
    begin
      forwardaE = 2'b00; forwardbE = 2'b00;
      if (rsE != 0)
        if (rsE == writeregM & regwriteM) forwardaE = 2'b10;
        else if (rsE == writeregW & regwriteW) forwardaE = 2'b01;
      if (rtE != 0)
        if (rtE == writeregM & regwriteM) forwardbE = 2'b10;
        else if (rtE == writeregW & regwriteW) forwardbE = 2'b01;
    end

  // forwarding result to M stage (data memory)
  assign forwardM = memwriteM & ~memwriteW & (writeregM == writeregW);

  // stalls
  assign #1 lwstallD = memtoregE & ~memwriteD & ((writeregE == rsD & rsD != 0) | (writeregE == rtD & rtD != 0));
  assign #1 cp0stallD = cp0ToRegE & ((writeregE == rsD & rsD != 0) | (writeregE == rtD & rtD != 0));
  assign #1 branchstallD = branchD & 
             (regwriteE & ((writeregE == rsD & rsD != 0) | (writeregE == rtD & rtD != 0)) |
              memtoregM & ((writeregM == rsD & rsD != 0) | (writeregM == rtD & rtD != 0)));
  assign #1 jumpstallD = jumpD[1] & 
             (regwriteE & ((writeregE == rsD & rsD != 0)) |
              memtoregM & ((writeregM == rsD & rsD != 0)));

    assign #1 stallM = ~dataAckM & (memtoregM | memwriteM);
                                            // wait for the data to be ready
    assign #1 flushM = cp0JumpM;            // clear every stage on interrupt/exception
    assign #1 flushW = stallM | cp0JumpM;   // stop garbage from propogating/clear stages
    assign #1 stallE = stallM;              // later pipeline stall
    assign #1 stallD = (lwstallD | cp0stallD | branchstallD | jumpstallD) | stallE;
                                            // wait for register value to calculate address, or later stall
    assign #1 flushE = (stallD & ~stallE) | cp0JumpM;    // stop garbage if it will propogate/clear stages
    assign #1 stallF = (~instrAckF | stallD) & ~cp0JumpM; // instruction not ready on bus, or later stall, unless exception/interrupt
    assign #1 flushD = (~stallD & (stallF | (|jumpD | pcsrcD))) | cp0JumpM;
                                            // stop garbage if it will propogate, or pc misprediction (not pc+4)/clear stages

endmodule
